﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Viewer3D
{
    public partial class Form1 : Form
    {
        private Camera camera;
        private Rotation3 rotation;             //相对物体坐标系原点的旋转
        private Rotation3 rotation_backup;      //交互过程中的备份
        private Point3 translation;             //交互过程物体坐标系相对世界坐标系的偏移
        private Point3 translation_backup;      //交互过程中的备份
        Point3 Ux = new Point3(1, 0, 0);        //世界坐标系x轴单位向量
        Point3 Uy = new Point3(0, 1, 0);        //世界坐标系y轴单位向量
        Point3 Uz = new Point3(0, 0, -1);       //世界坐标系z轴单位向量
        Point3 offset = new Point3(0, 0, 4000); //世界坐标系相对相机坐标系间的偏移关系

        Point x_axis, y_axis, z_axis;               //世界坐标系在图像上的坐标
        //Point aixs_location = new Point(40, 40);    //坐标系在图上的绘制位置
        //Point2 origin_t = new Point2(40, 40);
        Point center;

        ViewPoint[] vps;
        //Bitmap canvas;
        //Bitmap canvas_bk;                              //图形显示容器
        Point loc_mousedown;                        //记录按下鼠标时的位置
        bool isDraw = false;
        enum DrawMode { Translation, Rotation }
        DrawMode mode;
        int width, height;

        public Form1()
        {
            InitializeComponent();
            width = pbViewBox.Width;
            height = pbViewBox.Height;
            Bitmap canvas = new Bitmap(width, height);

            vps = new ViewPoint[0];

            camera = new Camera(2000, 2000, width / 2, height / 2);
            center = new Point(width / 2, height / 2);
            rotation = new Rotation3();
            rotation_backup = new Rotation3();
            translation = new Point3(0, 0, 0);
            translation_backup = new Point3(0, 0, 0);

            Graphics g = Graphics.FromImage(canvas);
            g.Clear(Color.White);
            pbViewBox.Image = canvas;

            UpdateAxis();           
            DrawAxis();
        }

        private void pbViewBox_MouseEnter(object sender, EventArgs e)
        {
            Cursor = Cursors.Hand;
        }

        private void pbViewBox_MouseLeave(object sender, EventArgs e)
        {
            Cursor = Cursors.Arrow;
        }

        private void pbViewBox_MouseUp(object sender, MouseEventArgs e)
        {
            isDraw = false;
            rotation_backup.Copy(rotation);
            translation_backup.Copy(translation);
        }

        private void pbViewBox_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0)
                offset.Z -= 10f;
            else
                offset.Z += 10f;

            //UpdateAxis();
            //DrawAxis();
        }

        private void pbViewBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDraw)
            {
                switch(mode)
                {
                    case DrawMode.Translation:
                        GetTranslation(e);
                        Reproject();
                        RefreshCanvas();
                        break;
                    case DrawMode.Rotation:
                        GetRotation(e);                        
                        Reproject();
                        RefreshCanvas();
                        break;
                }
            }
        }

        private void pbViewBox_MouseDown(object sender, MouseEventArgs e)
        {
            if(e.Button == MouseButtons.Left)
            {
                isDraw = true;
                loc_mousedown = e.Location;
                mode = DrawMode.Translation;
            }
            if(e.Button == MouseButtons.Right)
            {
                isDraw = true;
                loc_mousedown = e.Location;
                mode = DrawMode.Rotation;
            }
        }

        private void UpdateAxis()
        {
            Point2 ux, uy, uz;
            ux = camera.Project(rotation * Ux + new Point3(0, 0, 100));
            uy = camera.Project(rotation * Uy + new Point3(0, 0, 100));
            uz = camera.Project(rotation * Uz + new Point3(0, 0, 100));
            x_axis.X = (int)ux.X;
            x_axis.Y = (int)ux.Y;
            y_axis.X = (int)uy.X;
            y_axis.Y = (int)uy.Y;
            z_axis.X = (int)uz.X;
            z_axis.Y = (int)uz.Y;
        }

        private void DrawAxis()
        {
            Bitmap buff = (Bitmap)pbViewBox.Image;
            Graphics g = Graphics.FromImage(buff);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Pen pen;
            pen = new Pen(Color.Red);
            pen.Width = 3;
            g.DrawLine(pen, center, x_axis);
            pen.Color = Color.Green;
            g.DrawLine(pen, center, y_axis);
            pen.Color = Color.Blue;
            g.DrawLine(pen, center, z_axis);
            pbViewBox.Image = buff;
        }

        //private void DrawPoints(Dictionary<Point,Color> pixels)
        //{
        //    Bitmap buff = (Bitmap)pbViewBox.Image;
        //    foreach(var item in pixels)
        //    {
        //        buff.SetPixel(item.Key.X, item.Key.Y, item.Value);
        //    }
        //    pbViewBox.Image = buff;
        //}

        private void DrawLine(Point p1, Point p2, Color color)
        {
            Bitmap buff = (Bitmap)pbViewBox.Image;
            Graphics g = Graphics.FromImage(buff);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Pen pen = new Pen(color);
            g.DrawLine(pen, p1, p2);
            pbViewBox.Image = buff;
            g.Dispose();
        }

        private void DrawLinePolygon(Point[] pts, Color color)
        {
            Bitmap buff = (Bitmap)pbViewBox.Image;
            Graphics g = Graphics.FromImage(buff);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Pen pen = new Pen(color);
            g.DrawPolygon(pen, pts);
            pbViewBox.Image = buff;
            g.Dispose();
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            width = pbViewBox.Width;
            height = pbViewBox.Height;
            camera.SetImageSize(width, height);
            center.X = width / 2;
            center.Y = height / 2;
            Bitmap bm = new Bitmap(width, height);
            pbViewBox.Image = bm;
            Reproject();
            RefreshCanvas();           
            GC.Collect();
        }

        private void GetTranslation(MouseEventArgs e)
        {
            translation.X = translation_backup.X + (e.X - loc_mousedown.X);
            translation.Y = translation_backup.X + (e.Y - loc_mousedown.Y);
        }

        private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog fd = new OpenFileDialog();
            fd.Filter = "STL|*.stl|点云|*.ply";
            string filePath;
            if (fd.ShowDialog() == DialogResult.OK)
            {
                filePath = fd.FileName;
                StlModel md = new StlModel(filePath);
                md.Save("test_result_binary.stl",StlModel.SaveType.BINARY);
                md.Save("test_result_ascii.stl", StlModel.SaveType.ASCII);
            }           
        }

        private void GetRotation(MouseEventArgs e)
        {
            Point2 displcacement = new Point2(e.X - loc_mousedown.X, e.Y - loc_mousedown.Y);
            Point2 axe2d = displcacement.Unitary().Rotate(90);
            Point3 axe3d = new Point3(axe2d.X, axe2d.Y, 0);
            double angle = Math.Atan2(displcacement.Norm(), offset.Z/50) / Math.PI * 180;
            Rotation3 rot_tmp = new Rotation3(axe3d, angle);
            rotation = rot_tmp * rotation_backup;
        }

        private void ReadViewPoint(object filePath)
        {

        }

        private void RefreshCanvas()
        {
            Bitmap buff = (Bitmap)pbViewBox.Image;
            Graphics g = Graphics.FromImage(buff);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.Clear(Color.White);
            foreach (var vp in vps)
            {
                buff.SetPixel((int)vp.ICS.X, (int)vp.ICS.Y, vp.Color);
            }
            pbViewBox.Image = buff;

            DrawAxis();
        }

        private void Reproject()
        {
            foreach (var vp in vps)
            {
                vp.ICS = camera.Project(rotation_backup * vp.WCS + offset + translation_backup);
            }

            UpdateAxis();
        }
    }
}
